<template>
  <div class="light-cube">
    <h1>点光源立方体</h1>
    <p>这个示例展示了WebGL中基础的点光源光照效果，包括环境光和漫反射光照。通过调整光照参数，可以观察不同光照条件下立方体的渲染效果。</p>

    <el-card class="demo-section">
      <template #header>
        <div class="card-header">
          <h3>光照效果演示</h3>
        </div>
      </template>
      <div class="demo-content">
        <canvas ref="demoCanvas" width="400" height="300"></canvas>
        <div class="control-panel">
          <el-form :model="lightForm" label-width="120px">
            <el-form-item label="环境光强度">
              <el-slider
                v-model="lightForm.ambientIntensity"
                :min="0"
                :max="1"
                :step="0.1"
                @input="updateLight"
              />
            </el-form-item>
            <el-form-item label="漫反射光强度">
              <el-slider
                v-model="lightForm.diffuseIntensity"
                :min="0"
                :max="1"
                :step="0.1"
                @input="updateLight"
              />
            </el-form-item>
            <el-form-item label="光源位置 X">
              <el-slider
                v-model="lightForm.lightPosX"
                :min="-5"
                :max="5"
                :step="0.5"
                @input="updateLight"
              />
            </el-form-item>
            <el-form-item label="光源位置 Y">
              <el-slider
                v-model="lightForm.lightPosY"
                :min="-5"
                :max="5"
                :step="0.5"
                @input="updateLight"
              />
            </el-form-item>
            <el-form-item label="光源位置 Z">
              <el-slider
                v-model="lightForm.lightPosZ"
                :min="-5"
                :max="5"
                :step="0.5"
                @input="updateLight"
              />
            </el-form-item>
          </el-form>
          <div class="light-info">当前光源位置: ({{ lightForm.lightPosX }}, {{ lightForm.lightPosY }}, {{ lightForm.lightPosZ }})</div>
        </div>
      </div>
    </el-card>

    <el-card class="code-section">
      <template #header>
        <div class="card-header">
          <h3>示例代码</h3>
        </div>
      </template>
      <div class="code-content">
        <pre><code>
// 顶点着色器
{{ VSHADER_SOURCE }}

// 片元着色器
{{ FSHADER_SOURCE }}
        </code></pre>
      </div>
    </el-card>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, reactive, onUnmounted } from 'vue'
import { WebGLUtils } from '@/utils/webgl'
import { mat4 } from 'gl-matrix'

const demoCanvas = ref<HTMLCanvasElement | null>(null)
let gl: WebGLRenderingContext | null = null
let program: WebGLProgram | null = null
let webglUtils: WebGLUtils | null = null

const lightForm = reactive({
  ambientIntensity: 0.2,
  diffuseIntensity: 0.8,
  lightPosX: 3,
  lightPosY: 3,
  lightPosZ: 5
})

// 顶点着色器
const VSHADER_SOURCE = `
  attribute vec4 a_Position;
  attribute vec4 a_Normal;
  uniform mat4 u_MvpMatrix;
  uniform mat4 u_ModelMatrix;
  uniform mat4 u_NormalMatrix;
  varying vec3 v_Normal;
  varying vec3 v_Position;
  void main() {
    gl_Position = u_MvpMatrix * a_Position;
    v_Position = vec3(u_ModelMatrix * a_Position);
    v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));
  }
`

// 片元着色器
const FSHADER_SOURCE = `
  precision mediump float;
  uniform vec3 u_LightPosition;
  uniform vec3 u_AmbientLight;
  uniform vec3 u_LightColor;
  varying vec3 v_Normal;
  varying vec3 v_Position;
  void main() {
    vec3 normal = normalize(v_Normal);
    vec3 lightDirection = normalize(u_LightPosition - v_Position);
    float nDotL = max(dot(lightDirection, normal), 0.0);
    vec3 diffuse = u_LightColor * nDotL;
    vec3 ambient = u_AmbientLight;
    vec3 baseColor = vec3(0.8, 0.0, 0.0);
    gl_FragColor = vec4((diffuse + ambient) * baseColor, 1.0);
  }
`

function initVertexBuffers(gl: WebGLRenderingContext): number {
  // 创建立方体顶点和法向量数据
  const vertices = new Float32Array([
    // 前面
    1.0, 1.0, 1.0,  -1.0, 1.0, 1.0,  -1.0,-1.0, 1.0,   1.0,-1.0, 1.0,
    // 后面
    1.0, 1.0,-1.0,  -1.0, 1.0,-1.0,  -1.0,-1.0,-1.0,   1.0,-1.0,-1.0,
    // 顶面
    1.0, 1.0, 1.0,   1.0, 1.0,-1.0,  -1.0, 1.0,-1.0,  -1.0, 1.0, 1.0,
    // 底面
    1.0,-1.0, 1.0,   1.0,-1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0,-1.0, 1.0,
    // 右面
    1.0, 1.0, 1.0,   1.0,-1.0, 1.0,   1.0,-1.0,-1.0,   1.0, 1.0,-1.0,
    // 左面
   -1.0, 1.0, 1.0,  -1.0,-1.0, 1.0,  -1.0,-1.0,-1.0,  -1.0, 1.0,-1.0
  ])

  const normals = new Float32Array([
    // 前面
    0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,
    // 后面
    0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0,
    // 顶面
    0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,
    // 底面
    0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,
    // 右面
    1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,
    // 左面
   -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0
  ])

  const indices = new Uint8Array([
    0, 1, 2,   0, 2, 3,  // 前面
    4, 5, 6,   4, 6, 7,  // 后面
    8, 9,10,   8,10,11,  // 顶面
    12,13,14,  12,14,15, // 底面
    16,17,18,  16,18,19, // 右面
    20,21,22,  20,22,23  // 左面
  ])

  // 创建缓冲区对象
  const indexBuffer = gl.createBuffer()
  const vertexBuffer = gl.createBuffer()
  const normalBuffer = gl.createBuffer()

  if (!indexBuffer || !vertexBuffer || !normalBuffer) {
    console.error('Failed to create buffers')
    return 0
  }

  // 将顶点坐标写入缓冲区对象
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)
  const a_Position = gl.getAttribLocation(program!, 'a_Position')
  gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, 0, 0)
  gl.enableVertexAttribArray(a_Position)

  // 将法向量写入缓冲区对象
  gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer)
  gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW)
  const a_Normal = gl.getAttribLocation(program!, 'a_Normal')
  gl.vertexAttribPointer(a_Normal, 3, gl.FLOAT, false, 0, 0)
  gl.enableVertexAttribArray(a_Normal)

  // 将顶点索引数据写入缓冲区对象
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW)

  return indices.length
}

function draw() {
  if (!gl || !program) return

  gl.clearColor(0.0, 0.0, 0.0, 1.0)
  gl.enable(gl.DEPTH_TEST)
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

  // 更新矩阵
  updateMatrices()

  // 设置光照参数
  const u_LightPosition = gl.getUniformLocation(program, 'u_LightPosition')
  const u_AmbientLight = gl.getUniformLocation(program, 'u_AmbientLight')
  const u_LightColor = gl.getUniformLocation(program, 'u_LightColor')

  gl.uniform3f(u_LightPosition, lightForm.lightPosX, lightForm.lightPosY, lightForm.lightPosZ)
  gl.uniform3f(u_AmbientLight, lightForm.ambientIntensity, lightForm.ambientIntensity, lightForm.ambientIntensity)
  gl.uniform3f(u_LightColor, lightForm.diffuseIntensity, lightForm.diffuseIntensity, lightForm.diffuseIntensity)

  // 绘制立方体
  gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0)
}

let n: number

let angle = 0
let animationId: number

function initGL() {
  if (!demoCanvas.value) return

  gl = demoCanvas.value.getContext('webgl')
  if (!gl) {
    console.error('Failed to get WebGL context')
    return
  }

  // 创建WebGL工具实例
  webglUtils = new WebGLUtils(gl)

  // 创建着色器
  const vertexShader = webglUtils.createShader(gl.VERTEX_SHADER, VSHADER_SOURCE)
  const fragmentShader = webglUtils.createShader(gl.FRAGMENT_SHADER, FSHADER_SOURCE)
  if (!vertexShader || !fragmentShader) {
    console.error('Failed to create shaders')
    return
  }

  // 创建程序并链接着色器
  program = webglUtils.createProgram(vertexShader, fragmentShader)
  if (!program) {
    console.error('Failed to create program')
    return
  }

  // 使用程序
  gl.useProgram(program)

  // 初始化顶点缓冲区
  n = initVertexBuffers(gl)

  // 设置初始光照参数
  const u_LightPosition = gl.getUniformLocation(program, 'u_LightPosition')
  const u_AmbientLight = gl.getUniformLocation(program, 'u_AmbientLight')
  const u_LightColor = gl.getUniformLocation(program, 'u_LightColor')

  if (!u_LightPosition || !u_AmbientLight || !u_LightColor) {
    console.error('Failed to get uniform locations')
    return
  }

  gl.uniform3f(u_LightPosition, lightForm.lightPosX, lightForm.lightPosY, lightForm.lightPosZ)
  gl.uniform3f(u_AmbientLight, lightForm.ambientIntensity, lightForm.ambientIntensity, lightForm.ambientIntensity)
  gl.uniform3f(u_LightColor, lightForm.diffuseIntensity, lightForm.diffuseIntensity, lightForm.diffuseIntensity)

  // 启动动画循环
  animate()
}

function animate() {
  angle = (angle + 1) % 360
  draw()
  animationId = requestAnimationFrame(animate)
}

onMounted(() => {
  initGL()
})

onUnmounted(() => {
  if (animationId) {
    cancelAnimationFrame(animationId)
  }
})
function updateMatrices() {
  if (!gl || !program) return

  // 初始化所有矩阵
  const mvpMatrix = mat4.create()
  const modelMatrix = mat4.create()
  const normalMatrix = mat4.create()
  const projMatrix = mat4.create()
  const viewMatrix = mat4.create()

  // 设置模型矩阵（添加旋转）
  mat4.identity(modelMatrix)
  mat4.rotate(modelMatrix, modelMatrix, angle * Math.PI / 180, [0.5, 1, 0.2])

  // 设置透视投影
  const fov = 45 * Math.PI / 180
  const aspect = gl.canvas.width / gl.canvas.height
  const near = 0.1
  const far = 100.0
  mat4.perspective(projMatrix, fov, aspect, near, far)

  // 设置视图矩阵
  mat4.lookAt(viewMatrix,
    [0, 0, 6],  // 相机位置
    [0, 0, 0],  // 观察点
    [0, 1, 0]   // 上方向
  )

  // 计算MVP矩阵
  mat4.multiply(mvpMatrix, projMatrix, viewMatrix)
  mat4.multiply(mvpMatrix, mvpMatrix, modelMatrix)

  // 计算法向量矩阵（模型矩阵的逆转置矩阵）
  mat4.invert(normalMatrix, modelMatrix)
  mat4.transpose(normalMatrix, normalMatrix)

  // 传递矩阵到着色器
  const u_MvpMatrix = gl.getUniformLocation(program, 'u_MvpMatrix')
  const u_ModelMatrix = gl.getUniformLocation(program, 'u_ModelMatrix')
  const u_NormalMatrix = gl.getUniformLocation(program, 'u_NormalMatrix')

  gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix)
  gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix)
  gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix)
}

function updateLight() {
  draw()
}
</script>

<style scoped>
.light-cube {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

h1 {
  color: #303133;
  margin-bottom: 20px;
}

p {
  line-height: 1.6;
  color: #606266;
  margin-bottom: 20px;
}

.demo-section {
  margin-bottom: 20px;
}

.card-header {
  display: flex;
  align-items: center;
}

.card-header h3 {
  margin: 0;
  color: #409EFF;
}

.demo-content {
  display: flex;
  gap: 20px;
}

canvas {
  border: 1px solid #dcdfe6;
  background-color: #000;
}

.control-panel {
  flex: 1;
  max-width: 400px;
  padding: 20px;
  background-color: #f8f9fa;
  border-radius: 4px;
}

.light-info {
  margin-top: 15px;
  color: #606266;
  font-size: 14px;
}

.code-section {
  margin-top: 20px;
}

.code-content {
  background-color: #f8f9fa;
  padding: 15px;
  border-radius: 4px;
}

code {
  font-family: monospace;
  color: #409EFF;
}

pre {
  margin: 0;
  white-space: pre-wrap;
}
</style>
